package com.ruoyi.system.utils;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.urllink.GenerateUrlLinkRequest;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.redis.RedisCache;
import me.chanjar.weixin.common.error.WxErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @ProjectName: back
 * @Package: com.ruoyi.system.utils
 * @ClassName: WeChatTokenUtil
 * @Author: haiyue
 * @Description:
 * @Date: 2025/6/10 下午 7:11:55
 * @Version: 1.0
 */
@Component
public class WeChatUtil {

    private static final Logger log = LoggerFactory.getLogger(WeChatUtil.class);

    @Resource
    private RestTemplate restTemplate;
    @Resource
    private RedisCache redisCache;

    private final WxMaService wxMaService;

    @Value("${wechat.appId}")
    private String appId;

    @Value("${wechat.secret}")
    private String secret;

    @Value("${wechat.tokenUrl}")
    private String tokenUrl;

    @Value("${wechat.login-url}")
    private String jsCode2SessionUrl;
    @Value("${wechat.phone-number-url}")
    private String phoneNumberUrl;

    @Value("${wechat.access-token-expire}")
    private Integer expireTime;

    private static final String ACCESS_TOKEN_KEY = "wechat:access_token";

    @Autowired
    public WeChatUtil(WxMaService wxMaService) {
        this.wxMaService = wxMaService;
    }

    /**
     * 获取微信接口调用凭证（access_token）
     *
     * @return access_token字符串
     */
    public String getAccessToken() {
        // 先从Redis获取缓存的token
        String accessToken = redisCache.getCacheObject(ACCESS_TOKEN_KEY);
        if (Objects.nonNull(accessToken)) {
            return accessToken;
        }

        // 缓存中不存在，调用微信接口获取
        String newToken = fetchAccessTokenFromWechat();
        if (Objects.nonNull(newToken)) {
            // 存入Redis并设置过期时间（比微信默认时间少10分钟，避免刚好过期）
            redisCache.setCacheObject(ACCESS_TOKEN_KEY, newToken, expireTime - 600, TimeUnit.SECONDS);
        }
        return newToken;
    }

    /**
     * 调用微信接口获取access_token
     */
    private String fetchAccessTokenFromWechat() {
        try {
            // 拼接请求参数
            Map<String, String> params = new HashMap<>();
            params.put("grant_type", "client_credential");
            params.put("appid", appId);
            params.put("secret", secret);

            // 构建请求URL（使用RestTemplate的参数拼接方式，避免手动编码错误）
            String url = tokenUrl + "?grant_type={grant_type}&appid={appid}&secret={secret}";
            String result = restTemplate.getForObject(url, String.class, params);

            // 解析返回结果
            if (result.contains("errcode")) {
                // 处理错误
                int errCode = Integer.parseInt(result.substring(result.indexOf("errcode") + 8, result.indexOf(",")));
                String errMsg = result.substring(result.indexOf("errmsg") + 8, result.indexOf("\"", result.indexOf("errmsg") + 9));
                log.error("获取微信access_token失败，错误码：{}，错误信息：{}", errCode, errMsg);
                return null;
            }

            String accessToken = result.substring(result.indexOf("access_token") + 14, result.indexOf(",", result.indexOf("access_token") + 14));
            return accessToken.trim();
        } catch (Exception e) {
            log.error("获取微信access_token接口异常", e);
            return null;
        }
    }

    /**
     * 根据login.code获取用户openId
     *
     * @param jsCode
     * @return
     */
    public Map<String, String> codeSession(String jsCode) {
        Map<String, String> map = new HashMap<>();
        // 1. 调用微信接口获取openid和session_key
        String wxUrl = jsCode2SessionUrl + "?appid=" + appId + "&secret=" + secret + "&js_code=" + jsCode + "&grant_type=authorization_code";
        String wxResult = restTemplate.getForObject(wxUrl, String.class);
        JSONObject wxJson = JSONObject.parseObject(wxResult);

        if (wxJson.containsKey("errcode")) {
            map.put("result", "fail");
            map.put("errcode", wxJson.getIntValue("errcode") + "");
            map.put("errmsg", wxJson.getString("errmsg"));
            return map;
        }

        String openid = wxJson.getString("openid");
        String sessionKey = wxJson.getString("session_key");
        map.put("result", "success");
        map.put("openid", openid);
        map.put("sessionKey", sessionKey);

        return map;
    }

    /**
     * 获取用户手机号（通过前端code换取）
     *
     * @param code 前端wx.getPhoneNumber返回的code
     * @return 手机号信息（明文）
     */
    public String getPhoneNumber(String code) {
        try {
            // 直接使用code调用接口（SDK内部会自动管理session_key）
            WxMaPhoneNumberInfo phoneInfo = wxMaService.getUserService().getPhoneNoInfo(code);
            return phoneInfo.getPurePhoneNumber();
        } catch (Exception e) {
            log.error("获取用户手机号异常", e);
            return null;
        }
    }

    /**
     * generateUrlLink 获取加密URLLink
     *
     * @param path  地址
     * @param query 查询条件
     */
    public String generateUrlLink(String path, String query) throws WxErrorException {
        GenerateUrlLinkRequest gul = new GenerateUrlLinkRequest();
        gul.setPath(path);
        gul.setQuery(query);
        gul.setExpireType(0);

        LocalDateTime localDateTime = LocalDateTime.now().plusDays(29);
        ZoneId zoneId = ZoneId.of("UTC"); // 使用UTC时区
        ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
        long unixTimestamp = zonedDateTime.toInstant().toEpochMilli();
        gul.setExpireTime(unixTimestamp);
        gul.setEnvVersion("release");
//        gul.setEnvVersion("develop");
        return wxMaService.getLinkService().generateUrlLink(gul);
    }

    /**
     * URLLink 查询加密URLLink
     *
     * @param urlLink   小程序加密 url_link
     * @param queryType 查询类型。默认值0，查询 url_link 信息：0， 查询每天剩余访问次数：1
     */
    public String queryUrlLink(String urlLink, Long queryType) {
        String url = "https://api.weixin.qq.com/wxa/query_urllink?access_token=" + fetchAccessTokenFromWechat();
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("url_link", urlLink);
        paramMap.put("query_type", queryType);
        return HttpUtils.doGetPost(url, "POST", paramMap);
    }

}
